<?php

namespace App\Exceptions;

use App\Facades\Response;
use Illuminate\Support\Facades\Log;
use Throwable;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpFoundation\Response as FoundationResponse;


class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array<int, class-string<Throwable>>
     */
    protected $dontReport = [
        RepoException::class
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
//        'current_password',
        'password',
        'password_confirmation',
    ];

    public function report(Throwable $e)
    {
        parent::report($e);
    }

    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->reportable(function (Throwable $e) {
            //
        });
    }

    /**
     *  自定义异常信息
     *
     * @param $request
     * @param Throwable $exception
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response|mixed|FoundationResponse
     * @author  
     * @time 2024-01-16 15:40:44
     */
    public function render($request, Throwable $exception)
    {
        Log::info($exception);
        if ($exception instanceof AuthorizationException) {
            // 403
            $code = FoundationResponse::HTTP_FORBIDDEN;
            $msg = '资源不存在或没有权限访问，请尝试刷新重试！';
        } elseif ($exception instanceof AuthorizationRepoException) {
            //自定义无权限访问异常
            // 403
            $code = FoundationResponse::HTTP_FORBIDDEN;
            $msg = $exception->getMessage();
        } elseif ($exception instanceof RepoException) {
            //自定义异常
            // 200
            $code = FoundationResponse::HTTP_OK;
            $msg = $exception->getMessage();
        } elseif ($exception instanceof TokenException) {
            //token异常
            // 401
            $code = FoundationResponse::HTTP_UNAUTHORIZED;
            $msg = $exception->getMessage();
        } else {
            $code = method_exists($exception, 'getStatusCode') ? $exception->getStatusCode() : $exception->getCode();
            if ($code == FoundationResponse::HTTP_BAD_REQUEST) { // 参数错误 400
                $payload = json_decode($exception->getMessage(), true);
                $msg = $payload['msg'] ?? $exception->getMessage();
                $errors = $payload['errors'];
            } else {
                $msg = $exception->getMessage();
            }
        }

        $response = [
            'msg' => $msg,
            'code' => $code,
        ];

        [$code, $msg] = $this->formatErrorMsg($request, $response, $exception);

        if (config('app.env') !== 'production') {
            $response['file'] = $exception->getFile();
            $response['line'] = $exception->getLine();
        }
        $response['errors'] = $errors ?? [];

        return Response::fail(
            $msg,
            $response,
            $code < 500 && isset(FoundationResponse::$statusTexts[$code]) ? $code : FoundationResponse::HTTP_BAD_REQUEST
        );
    }


    /**
     * 特殊报错处理，前端隐藏真实报错信息，并实时推送报错信息到钉钉
     *
     * @param $request
     * @param array $response
     * @param Throwable $exception
     * @return array
     * @author  
     * @time 2024-01-16 15:41:09
     */
    private function formatErrorMsg($request, array $response, Throwable $exception): array
    {
        $msg = $response['msg']; // 前端展示的报错信息
        $code = $response['code'];

        if (
            strpos($msg, 'SQLSTATE') === 0
            || strpos($msg, 'PDOStatement') === 0
            || strpos($msg, 'Packets out of order') === 0
        ) {
            // 数据库报错处理
            if (strpos($msg, 'Duplicate entry') === false) { // 忽略唯一键冲突的报错
                $msg = "# 数据库访问异常\n";
                $specialError = [
                    'Too many connections',
                    'Connection timed out',
                    'Access denied for user',
                    'Lost connection to backend server',
                    'Column not found',
                    'Integrity constraint violation',
                ];
                foreach ($specialError as $err) {
                    if (strpos($msg, $err) !== false) {
                        $msg .= "{$err}（注意：可能存在大量相同的报错，具体请看日志！！！）\n";
                        break;
                    }
                }
            }
            $msg = '系统出现异常，请稍等一分钟后再刷新重试！';
        } elseif (
            strpos($msg, 'Client error') !== false
            || strpos($msg, 'Server error') !== false
            || strpos($msg, 'cURL error') !== false
        ) {
            // 第三方接口报错处理：防止出现 401 token 错误导致整个系统无法正常登录使用
//            $msg = "# 第三方系统接口调用异常\n";
            $msg = "系统繁忙，请稍后。\n";
            $code = FoundationResponse::HTTP_BAD_REQUEST;

        } elseif (strpos($msg, 'Allowed memory size') !== false) {
            $msg = "# 内存溢出异常\n";
        } elseif ($exception instanceof \ErrorException) {
            $msg = "# ErrorException\n";
        } elseif (mb_strlen($msg) > 100) {
            $msg = "# Error Message too long\n";
        }

        // 通知钉钉群


        return [$code, $msg];
    }
}
